From 4e87400732c77765afae2ea89ed43837457aa604 Mon Sep 17 00:00:00 2001
From: Rajith Cherian <rajith@codeaurora.org>
Date: Wed, 1 Feb 2017 19:00:26 +0530
Subject: [PATCH] ipq8064: tsens: Support for configurable interrupts

Provide support for adding configurable high and
configurable low trip temperatures. An interrupts is
also triggerred when these trip points are hit. The
interrupts can be activated or deactivated from sysfs.
This functionality is made available only if
CONFIG_THERMAL_WRITABLE_TRIPS is defined.

Change-Id: Ib73f3f9459de4fffce7bb985a0312a88291f4934
Signed-off-by: Rajith Cherian <rajith@codeaurora.org>
---
 .../devicetree/bindings/thermal/qcom-tsens.txt     |  4 ++
 drivers/thermal/of-thermal.c                       | 63 ++++++++++++++++++----
 drivers/thermal/qcom/tsens.c                       | 43 ++++++++++++---
 drivers/thermal/qcom/tsens.h                       | 11 ++++
 drivers/thermal/thermal_core.c                     | 44 ++++++++++++++-
 include/linux/thermal.h                            | 14 +++++
 6 files changed, 162 insertions(+), 17 deletions(-)

--- a/Documentation/devicetree/bindings/thermal/qcom-tsens.txt
+++ b/Documentation/devicetree/bindings/thermal/qcom-tsens.txt
@@ -12,11 +12,15 @@ Required properties:
 - Refer to Documentation/devicetree/bindings/nvmem/nvmem.txt to know how to specify
 nvmem cells
 
+Optional properties:
+- interrupts: Interrupt which gets triggered when threshold is hit
+
 Example:
 tsens: thermal-sensor@900000 {
 		compatible = "qcom,msm8916-tsens";
 		reg = <0x4a8000 0x2000>;
 		nvmem-cells = <&tsens_caldata>, <&tsens_calsel>;
 		nvmem-cell-names = "caldata", "calsel";
+		interrupts = <0 178 0>;
 		#thermal-sensor-cells = <1>;
 	};
--- a/drivers/thermal/of-thermal.c
+++ b/drivers/thermal/of-thermal.c
@@ -95,7 +95,7 @@ static int of_thermal_get_temp(struct th
 {
 	struct __thermal_zone *data = tz->devdata;
 
-	if (!data->ops->get_temp)
+	if (!data->ops->get_temp || (data->mode == THERMAL_DEVICE_DISABLED))
 		return -EINVAL;
 
 	return data->ops->get_temp(data->sensor_data, temp);
@@ -106,7 +106,8 @@ static int of_thermal_set_trips(struct t
 {
 	struct __thermal_zone *data = tz->devdata;
 
-	if (!data->ops || !data->ops->set_trips)
+	if (!data->ops || !data->ops->set_trips
+			|| (data->mode == THERMAL_DEVICE_DISABLED))
 		return -EINVAL;
 
 	return data->ops->set_trips(data->sensor_data, low, high);
@@ -192,6 +193,9 @@ static int of_thermal_set_emul_temp(stru
 {
 	struct __thermal_zone *data = tz->devdata;
 
+	if (data->mode == THERMAL_DEVICE_DISABLED)
+		return -EINVAL;
+
 	return data->ops->set_emul_temp(data->sensor_data, temp);
 }
 
@@ -200,7 +204,7 @@ static int of_thermal_get_trend(struct t
 {
 	struct __thermal_zone *data = tz->devdata;
 
-	if (!data->ops->get_trend)
+	if (!data->ops->get_trend || (data->mode == THERMAL_DEVICE_DISABLED))
 		return -EINVAL;
 
 	return data->ops->get_trend(data->sensor_data, trip, trend);
@@ -289,7 +293,9 @@ static int of_thermal_set_mode(struct th
 	mutex_unlock(&tz->lock);
 
 	data->mode = mode;
-	thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED);
+
+	if (mode == THERMAL_DEVICE_ENABLED)
+		thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED);
 
 	return 0;
 }
@@ -299,7 +305,8 @@ static int of_thermal_get_trip_type(stru
 {
 	struct __thermal_zone *data = tz->devdata;
 
-	if (trip >= data->ntrips || trip < 0)
+	if (trip >= data->ntrips || trip < 0
+				|| (data->mode == THERMAL_DEVICE_DISABLED))
 		return -EDOM;
 
 	*type = data->trips[trip].type;
@@ -307,12 +314,39 @@ static int of_thermal_get_trip_type(stru
 	return 0;
 }
 
+static int of_thermal_activate_trip_type(struct thermal_zone_device *tz,
+			int trip, enum thermal_trip_activation_mode mode)
+{
+	struct __thermal_zone *data = tz->devdata;
+
+	if (trip >= data->ntrips || trip < 0
+				|| (data->mode == THERMAL_DEVICE_DISABLED))
+		return -EDOM;
+
+	/*
+	 * The configurable_hi and configurable_lo trip points can be
+	 * activated and deactivated.
+	 */
+
+	if (data->ops->set_trip_activate) {
+		int ret;
+
+		ret = data->ops->set_trip_activate(data->sensor_data,
+								trip, mode);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+
 static int of_thermal_get_trip_temp(struct thermal_zone_device *tz, int trip,
 				    int *temp)
 {
 	struct __thermal_zone *data = tz->devdata;
 
-	if (trip >= data->ntrips || trip < 0)
+	if (trip >= data->ntrips || trip < 0
+				|| (data->mode == THERMAL_DEVICE_DISABLED))
 		return -EDOM;
 
 	*temp = data->trips[trip].temperature;
@@ -325,7 +359,8 @@ static int of_thermal_set_trip_temp(stru
 {
 	struct __thermal_zone *data = tz->devdata;
 
-	if (trip >= data->ntrips || trip < 0)
+	if (trip >= data->ntrips || trip < 0
+				|| (data->mode == THERMAL_DEVICE_DISABLED))
 		return -EDOM;
 
 	if (data->ops->set_trip_temp) {
@@ -347,7 +382,8 @@ static int of_thermal_get_trip_hyst(stru
 {
 	struct __thermal_zone *data = tz->devdata;
 
-	if (trip >= data->ntrips || trip < 0)
+	if (trip >= data->ntrips || trip < 0
+				|| (data->mode == THERMAL_DEVICE_DISABLED))
 		return -EDOM;
 
 	*hyst = data->trips[trip].hysteresis;
@@ -360,7 +396,8 @@ static int of_thermal_set_trip_hyst(stru
 {
 	struct __thermal_zone *data = tz->devdata;
 
-	if (trip >= data->ntrips || trip < 0)
+	if (trip >= data->ntrips || trip < 0
+				|| (data->mode == THERMAL_DEVICE_DISABLED))
 		return -EDOM;
 
 	/* thermal framework should take care of data->mask & (1 << trip) */
@@ -435,6 +472,9 @@ thermal_zone_of_add_sensor(struct device
 	if (ops->set_emul_temp)
 		tzd->ops->set_emul_temp = of_thermal_set_emul_temp;
 
+	if (ops->set_trip_activate)
+		tzd->ops->set_trip_activate = of_thermal_activate_trip_type;
+
 	mutex_unlock(&tzd->lock);
 
 	return tzd;
@@ -729,7 +769,10 @@ static const char * const trip_types[] =
 	[THERMAL_TRIP_ACTIVE]	= "active",
 	[THERMAL_TRIP_PASSIVE]	= "passive",
 	[THERMAL_TRIP_HOT]	= "hot",
-	[THERMAL_TRIP_CRITICAL]	= "critical",
+	[THERMAL_TRIP_CRITICAL]	= "critical_high",
+	[THERMAL_TRIP_CONFIGURABLE_HI] = "configurable_hi",
+	[THERMAL_TRIP_CONFIGURABLE_LOW] = "configurable_lo",
+	[THERMAL_TRIP_CRITICAL_LOW] = "critical_low",
 };
 
 /**
--- a/drivers/thermal/qcom/tsens.c
+++ b/drivers/thermal/qcom/tsens.c
@@ -31,7 +31,7 @@ static int tsens_get_temp(void *data, in
 
 static int tsens_get_trend(void *p, int trip, enum thermal_trend *trend)
 {
-	const struct tsens_sensor *s = p;
+	struct tsens_sensor *s = p;
 	struct tsens_device *tmdev = s->tmdev;
 
 	if (tmdev->ops->get_trend)
@@ -40,9 +40,10 @@ static int tsens_get_trend(void *p, int
 	return -ENOTSUPP;
 }
 
-static int  __maybe_unused tsens_suspend(struct device *dev)
+static int  __maybe_unused tsens_suspend(void *data)
 {
-	struct tsens_device *tmdev = dev_get_drvdata(dev);
+	struct tsens_sensor *s = data;
+	struct tsens_device *tmdev = s->tmdev;
 
 	if (tmdev->ops && tmdev->ops->suspend)
 		return tmdev->ops->suspend(tmdev);
@@ -50,9 +51,10 @@ static int  __maybe_unused tsens_suspend
 	return 0;
 }
 
-static int __maybe_unused tsens_resume(struct device *dev)
+static int __maybe_unused tsens_resume(void *data)
 {
-	struct tsens_device *tmdev = dev_get_drvdata(dev);
+	struct tsens_sensor *s = data;
+	struct tsens_device *tmdev = s->tmdev;
 
 	if (tmdev->ops && tmdev->ops->resume)
 		return tmdev->ops->resume(tmdev);
@@ -60,6 +62,30 @@ static int __maybe_unused tsens_resume(s
 	return 0;
 }
 
+static int  __maybe_unused tsens_set_trip_temp(void *data, int trip, int temp)
+{
+	struct tsens_sensor *s = data;
+	struct tsens_device *tmdev = s->tmdev;
+
+	if (tmdev->ops && tmdev->ops->set_trip_temp)
+		return tmdev->ops->set_trip_temp(s, trip, temp);
+
+	return 0;
+}
+
+static int __maybe_unused tsens_activate_trip_type(void *data, int trip,
+					enum thermal_trip_activation_mode mode)
+{
+	struct tsens_sensor *s = data;
+	struct tsens_device *tmdev = s->tmdev;
+
+	if (tmdev->ops && tmdev->ops->set_trip_activate)
+		return tmdev->ops->set_trip_activate(s, trip, mode);
+
+	return 0;
+}
+
+
 static SIMPLE_DEV_PM_OPS(tsens_pm_ops, tsens_suspend, tsens_resume);
 
 static const struct of_device_id tsens_table[] = {
@@ -83,6 +109,8 @@ MODULE_DEVICE_TABLE(of, tsens_table);
 static const struct thermal_zone_of_device_ops tsens_of_ops = {
 	.get_temp = tsens_get_temp,
 	.get_trend = tsens_get_trend,
+	.set_trip_temp = tsens_set_trip_temp,
+	.set_trip_activate = tsens_activate_trip_type,
 };
 
 static int tsens_register(struct tsens_device *tmdev)
@@ -131,7 +159,7 @@ static int tsens_probe(struct platform_d
 	if (id)
 		data = id->data;
 	else
-		data = &data_8960;
+		return -EINVAL;
 
 	if (data->num_sensors <= 0) {
 		dev_err(dev, "invalid number of sensors\n");
@@ -146,6 +174,9 @@ static int tsens_probe(struct platform_d
 	tmdev->dev = dev;
 	tmdev->num_sensors = data->num_sensors;
 	tmdev->ops = data->ops;
+
+	tmdev->tsens_irq = platform_get_irq(pdev, 0);
+
 	for (i = 0;  i < tmdev->num_sensors; i++) {
 		if (data->hw_ids)
 			tmdev->sensor[i].hw_id = data->hw_ids[i];
--- a/drivers/thermal/qcom/tsens.h
+++ b/drivers/thermal/qcom/tsens.h
@@ -24,9 +24,12 @@ struct tsens_device;
 struct tsens_sensor {
 	struct tsens_device		*tmdev;
 	struct thermal_zone_device	*tzd;
+	struct work_struct		notify_work;
 	int				offset;
 	int				id;
 	int				hw_id;
+	int				calib_data;
+	int				calib_data_backup;
 	int				slope;
 	u32				status;
 };
@@ -41,6 +44,9 @@ struct tsens_sensor {
  * @suspend: Function to suspend the tsens device
  * @resume: Function to resume the tsens device
  * @get_trend: Function to get the thermal/temp trend
+ * @set_trip_temp: Function to set trip temp
+ * @get_trip_temp: Function to get trip temp
+ * @set_trip_activate: Function to activate trip points
  */
 struct tsens_ops {
 	/* mandatory callbacks */
@@ -53,6 +59,9 @@ struct tsens_ops {
 	int (*suspend)(struct tsens_device *);
 	int (*resume)(struct tsens_device *);
 	int (*get_trend)(struct tsens_device *, int, enum thermal_trend *);
+	int (*set_trip_temp)(void *, int, int);
+	int (*set_trip_activate)(void *, int,
+					enum thermal_trip_activation_mode);
 };
 
 /**
@@ -76,11 +85,13 @@ struct tsens_context {
 struct tsens_device {
 	struct device			*dev;
 	u32				num_sensors;
+	u32				tsens_irq;
 	struct regmap			*map;
 	struct regmap_field		*status_field;
 	struct tsens_context		ctx;
 	bool				trdy;
 	const struct tsens_ops		*ops;
+	struct work_struct		tsens_work;
 	struct tsens_sensor		sensor[0];
 };
 
--- a/drivers/thermal/thermal_sysfs.c
+++ b/drivers/thermal/thermal_sysfs.c
@@ -115,12 +115,48 @@ trip_point_type_show(struct device *dev,
 		return sprintf(buf, "passive\n");
 	case THERMAL_TRIP_ACTIVE:
 		return sprintf(buf, "active\n");
+	case THERMAL_TRIP_CONFIGURABLE_HI:
+		return sprintf(buf, "configurable_hi\n");
+	case THERMAL_TRIP_CONFIGURABLE_LOW:
+		return sprintf(buf, "configurable_low\n");
+	case THERMAL_TRIP_CRITICAL_LOW:
+		return sprintf(buf, "critical_low\n");
 	default:
 		return sprintf(buf, "unknown\n");
 	}
 }
 
 static ssize_t
+trip_point_type_activate(struct device *dev, struct device_attribute *attr,
+						const char *buf, size_t count)
+{
+	struct thermal_zone_device *tz = to_thermal_zone(dev);
+	int trip, ret;
+	char *enabled = "enabled";
+	char *disabled = "disabled";
+
+	if (!tz->ops->set_trip_activate)
+		return -EPERM;
+
+	if (!sscanf(attr->attr.name, "trip_point_%d_type", &trip))
+		return -EINVAL;
+
+	if (!strncmp(buf, enabled, strlen(enabled)))
+		ret = tz->ops->set_trip_activate(tz, trip,
+				THERMAL_TRIP_ACTIVATION_ENABLED);
+	else if (!strncmp(buf, disabled, strlen(disabled)))
+		ret = tz->ops->set_trip_activate(tz, trip,
+				THERMAL_TRIP_ACTIVATION_DISABLED);
+	else
+		ret = -EINVAL;
+
+	if (ret)
+		return ret;
+
+	return count;
+}
+
+static ssize_t
 trip_point_temp_store(struct device *dev, struct device_attribute *attr,
 		      const char *buf, size_t count)
 {
@@ -562,6 +598,12 @@ static int create_trip_attrs(struct ther
 		tz->trip_type_attrs[indx].attr.show = trip_point_type_show;
 		attrs[indx] = &tz->trip_type_attrs[indx].attr.attr;
 
+		if (IS_ENABLED(CONFIG_THERMAL_WRITABLE_TRIPS)) {
+			tz->trip_type_attrs[indx].attr.store
+						= trip_point_type_activate;
+			tz->trip_type_attrs[indx].attr.attr.mode |= S_IWUSR;
+		}
+
 		/* create trip temp attribute */
 		snprintf(tz->trip_temp_attrs[indx].name, THERMAL_NAME_LENGTH,
 			 "trip_point_%d_temp", indx);
--- a/include/linux/thermal.h
+++ b/include/linux/thermal.h
@@ -78,11 +78,19 @@ enum thermal_device_mode {
 	THERMAL_DEVICE_ENABLED,
 };
 
+enum thermal_trip_activation_mode {
+	THERMAL_TRIP_ACTIVATION_DISABLED = 0,
+	THERMAL_TRIP_ACTIVATION_ENABLED,
+};
+
 enum thermal_trip_type {
 	THERMAL_TRIP_ACTIVE = 0,
 	THERMAL_TRIP_PASSIVE,
 	THERMAL_TRIP_HOT,
 	THERMAL_TRIP_CRITICAL,
+	THERMAL_TRIP_CONFIGURABLE_HI,
+	THERMAL_TRIP_CONFIGURABLE_LOW,
+	THERMAL_TRIP_CRITICAL_LOW,
 };
 
 enum thermal_trend {
@@ -120,6 +128,8 @@ struct thermal_zone_device_ops {
 		enum thermal_trip_type *);
 	int (*get_trip_temp) (struct thermal_zone_device *, int, int *);
 	int (*set_trip_temp) (struct thermal_zone_device *, int, int);
+	int (*set_trip_activate) (struct thermal_zone_device *, int,
+					enum thermal_trip_activation_mode);
 	int (*get_trip_hyst) (struct thermal_zone_device *, int, int *);
 	int (*set_trip_hyst) (struct thermal_zone_device *, int, int);
 	int (*get_crit_temp) (struct thermal_zone_device *, int *);
@@ -363,6 +373,8 @@ struct thermal_genl_event {
  *		   temperature.
  * @set_trip_temp: a pointer to a function that sets the trip temperature on
  *		   hardware.
+ * @activate_trip_type: a pointer to a function to enable/disable trip
+ *		temperature interrupts
  */
 struct thermal_zone_of_device_ops {
 	int (*get_temp)(void *, int *);
@@ -370,6 +382,8 @@ struct thermal_zone_of_device_ops {
 	int (*set_trips)(void *, int, int);
 	int (*set_emul_temp)(void *, int);
 	int (*set_trip_temp)(void *, int, int);
+	int (*set_trip_activate)(void *, int,
+				enum thermal_trip_activation_mode);
 };
 
 /**
